
/*
 *	Simulator package
 *	CPU
 */

#include "global.h"
#include "cpu.h"
#include "queue.h"
#include "pcb.h"
#include "interrupt_trap_handler.h"
#include "pcb_queue.h"
#include <stdio.h>
#include <unistd.h>

int (*event_list[10]) (PCBPtr*, int) = {
		timer_int_handler, io_int_handler, io_kybd_handler, io_req_trap_handler, mutex_lock_trap_handler,
		mutex_unlock_trap_handler, cond_wait_trap_handler, cond_signal_trap_handler, start_process, stop_process
};



int run_cpu(PCBPtr initial[], int proc_count) {
	ready_queue = newPcbQueueConstructor();
	waiting_queue = newPcbQueueConstructor();
	keyboard_queue = newPcbQueueConstructor();
	pending_interrupts = newQueueConstructor();
	pending_interrupts_lock = PTHREAD_MUTEX_INITIALIZER;
  
  //HERE YOU GO, CAPTAIN
	pthread_mutex_lock(&pending_interrupts_lock);
	pthread_mutex_unlock(&pending_interrupts_lock);
  
	PCBPtr current_process = NULL;

	int p_counter = 0;
	for(p_counter = 0; p_counter < MAX_CYCLE; p_counter++) {\

		int i;
		for(i = 0; i < proc_count; i++) {
			PCBPtr check_process = initial[i];
//printw("[err]inside CPU %d\n",i);
			if(check_process->getTimeSlice(check_process) == p_counter) {
				ready_queue->addToQ(ready_queue, check_process);
//				check_process->setReadyQueue(check_process, idle_process);

				check_process->setState(check_process, READY);
				if(current_process == NULL && !ready_queue->empty(ready_queue)) current_process = ready_queue->dequeueFromQ(ready_queue);
printw("[err]process pushed to ready queue\n");
			}
		}
      
//printw("[err]checked times %d\n", p_counter);

		int event_token = NO_EVENT;

      
		if(p_counter % TIMER_QUANTA == 0) {
			event_token = TIMER_INTERRUPT;
      printw("--------------------\n");
			printw("ready: ");ready_queue->display(ready_queue);
			printw("wait:  ");waiting_queue->display(waiting_queue);
			printw("kbd:   ");keyboard_queue->display(keyboard_queue);
      printw("--------------------\n");
			run_event(TIMER_INTERRUPT, &current_process, p_counter);
		} else {
      
			pthread_mutex_lock(&pending_interrupts_lock);
			event_token = call_interrupt_controller(p_counter);
			pthread_mutex_unlock(&pending_interrupts_lock);
      

		}

      
		if(event_token > TIMER_INTERRUPT && current_process != NULL) {
      
			run_event(event_token, &current_process, p_counter);
			event_token = NO_EVENT;
		}
 
		if(current_process != NULL && current_process->stepProcess != NULL) {
			int token = current_process->stepProcess(current_process);
      

			pthread_mutex_lock(&pending_interrupts_lock);
      

			if(token != NO_EVENT) pending_interrupts->enqueue(pending_interrupts, token);
      
			if(!(pending_interrupts->empty(pending_interrupts))) pending_interrupts->display(pending_interrupts);
      
      
			pthread_mutex_unlock(&pending_interrupts_lock);
      
		}
      
		
      usleep(120*1000);
	}
	// blank comment
printw("\nBy jove.  The CPU survived.");
	return 0;
}


int call_interrupt_controller(int pc) {
	if(pending_interrupts == NULL) return NO_EVENT;
	return pending_interrupts->dequeue(pending_interrupts) ;
}

/*
 * create a constant NEXT_PROCESS_TO_READY
 * When timer event kicks in, run_event will return this constant to tell the CPU to move the
 * head of the initial_q to the running or ready queue
 */
int run_event(int event, PCBPtr* running_process, int counter) {
	return event_list[event](running_process, counter);
}

int start_process(PCBPtr * joker, int batman) {
  return 5;
}

int stop_process(PCBPtr * penguin, int robin) {
  return 42;
}
